import logging
from datetime import datetime
from common.const import IntervalType
from common.timeutil import get_interval_end_pair
from common.mg_core.mg import insert_or_update
from common.es_core.es_interface import es_search_interface

_LOGGER = logging.getLogger(__name__)


def aggregate_data(interval_type, event_rule, index, base_time=None, last_window=False):
    # _LOGGER.info('begin aggregate interval data, interval_type: [{}], base_time: [{}], last_window: [{}]'.format(
    #    interval_type.value, base_time, last_window))
    start_time, end_time = get_interval_end_pair(interval_type, base_time, last_window)
    # _LOGGER.info('begin aggregate event: [{}]'.format(EVENT_RULES))
    aggregate_event_data(interval_type, event_rule, start_time, end_time, index)
    # _LOGGER.info('end aggregate event: [{}]'.format(EVENT_RULES))
    # _LOGGER.info('end aggregate interval data, interval_type: [{}], base_time: [{}], last_window: [{}]'.format(
    #    interval_type.value, base_time, last_window))


def aggregate_event_data(interval_type, event_rule, start_time, end_time, index):
    aggs_dsl = _get_aggs_dsl(event_rule, start_time, end_time)
    es_response = _process_es_response(aggs_dsl, index)
    result = _process_aggs_data(interval_type, event_rule, start_time, es_response['aggregations'], index)
    insert_or_update(result, database=index, collection=interval_type.value)


def _get_aggs_dsl(event_rule, start_time, end_time):
    event_id = event_rule['event_id']
    uv_id = event_rule['rule']['aggs']['uv']['field_id']
    value_fields = event_rule['rule']['aggs']['values']
    group_fields = event_rule['rule']['aggs']['groups']
    aggs_dsl = {
        "query": {
            "bool": {
                "filter": [
                    {"term": {"_event_id": event_id}},
                    {'range': {"_event_time": {"gte": start_time.timestamp(), "lt": end_time.timestamp()}}}
                ]
            }
        },
        "size": 0,
        "aggs": {
        }
    }
    if event_rule['rule']['aggs'].get("filter", []):
        aggs_dsl['query']['bool']['filter'] += event_rule['rule']['aggs'].get("filter", [])
    aggs_dsl['aggs'].update(_get_value_aggs_dsl(value_fields, uv_id, event_id))
    groups_dsl = dict()
    for group_field in group_fields:
        group_id = group_field['field_id']
        is_keyword = group_field.get('is_keyword', False)
        groups_dsl.update(_get_group_aggs_dsl(group_id, value_fields, uv_id, is_keyword, event_id))
        for sub_field in group_field.get('sub_fields', []):
            groups_dsl["group_{}".format(group_id)]['aggs'].update(
                _get_group_aggs_dsl(sub_field['field_id'], value_fields, uv_id, sub_field.get('is_keyword', False),
                                    event_id))
    aggs_dsl['aggs'].update(groups_dsl)

    return aggs_dsl


def _get_value_aggs_dsl(value_fields, uv_id, event_id):
    operation = "value_count" if event_id == "register" else "cardinality"
    result = {
        "pv": {
            "value_count": {"field": "_event_id.keyword"}
        },
        "uv": {
            operation: {"field": uv_id}
        }
    }
    for value_field in value_fields:
        if 'value_expr' in value_field:
            continue
        value_id = value_field['value_id']
        result.update({
            "sum_{}".format(value_id): {
                "sum": {
                    "field": value_id
                }
            }
        })
    return result


def _get_group_aggs_dsl(group_id, value_fields, uv_id, is_keyword, event_id):
    group_dsl = {
        "group_{}".format(group_id): {
            "terms": {
                "field": "{}.keyword".format(group_id) if is_keyword else group_id,
                "size": 10000
            },
            "aggs": _get_value_aggs_dsl(value_fields, uv_id, event_id)
        }
    }
    return group_dsl


def _process_es_response(criteria, index):
    return es_search_interface(index, criteria)


def _process_aggs_data(interval_type, event_rule, start_time, aggs_data, index):
    event_id = event_rule['event_id']
    value_config = event_rule['values']
    value_fields = event_rule['rule']['aggs']['values']
    group_fields = event_rule['rule']['aggs']['groups']
    day = datetime.strftime(start_time, '%Y-%m-%d')
    hour = str(str(start_time.hour)).zfill(2)
    doc_id = _generate_doc_id(event_id, start_time) if interval_type != IntervalType.ALL_TIME else event_id
    result = dict(_id=doc_id, event_id=event_id, base_time=start_time, day=day, hour=hour)
    result['data'] = _process_aggs_unit(value_fields, aggs_data)
    result['groups_data'] = dict()
    for group_field in group_fields:
        _process_group_aggs_unit(event_id, group_field, value_config, value_fields, aggs_data,
                                 result['groups_data'], index)
    return result


def _generate_doc_id(event_id, base_time):
    return '{}_{}'.format(event_id, datetime.strftime(base_time, '%Y-%m-%d-%H-%M-%S'))


def get_value_field_name(base_group_id, bucket_key, value_config, index, last_key=None):
    if 'inherent' in value_config.get(base_group_id, {}).get('enum', {}) and last_key:
        inherent_key = value_config.get(base_group_id, {}).get('enum', {}).get('inherent', {})
        target_enum_dct = value_config.get(inherent_key, {}).get('enum', {}).get('target_enum_dct', {})
        if target_enum_dct.get('platform_routing'):
            target_enum_dct = target_enum_dct.get(index, {})
        target_enum_dct = target_enum_dct.get(str(last_key), {}).get(base_group_id, {})
    else:
        target_enum_dct = value_config.get(base_group_id, {}).get('enum', {}).get('target_enum_dct', {})
        if target_enum_dct.get('platform_routing'):
            target_enum_dct = target_enum_dct.get(index, {})
    field_name = target_enum_dct.get(str(bucket_key), {}).get('name', bucket_key)
    return field_name


def _process_group_aggs_unit(event_id, group_field, value_config, value_fields,
                             aggs_data, groups_data, index, last_group_by_key=None):
    group_id = group_field['field_id']
    group_name = group_field['field_name']
    slots = group_field.get('slots')
    final_group_id = '{}_slots'.format(group_id) if slots else group_id
    group_data = {'data': {}, 'group_name': group_name}
    for bucket in aggs_data['group_{}'.format(final_group_id)]['buckets']:
        base_group_id = group_id.replace(event_id + '_', '').replace('.keyword', '')
        field_name = bucket['key']
        if base_group_id in value_config and 'enum' in value_config.get(base_group_id):
            field_name = get_value_field_name(base_group_id, bucket['key'], value_config, index, last_group_by_key)
        group_by_item = dict({'group_value': bucket['key'], 'data': _process_aggs_unit(value_fields, bucket),
                              'group_name': field_name,
                              'groups_data': {}})
        for sub_field in group_field.get('sub_fields', []):
            _process_group_aggs_unit(event_id, sub_field, value_config, value_fields, bucket,
                                     group_by_item['groups_data'], index, last_group_by_key=bucket['key'])
        group_data['data'][str(bucket['key'])] = group_by_item
        groups_data[final_group_id] = group_data


def _process_aggs_unit(value_fields, aggs_data):
    result = dict()
    result['pv'] = aggs_data['pv']['value']
    result['uv'] = aggs_data['uv']['value']
    for value_field in value_fields:
        if 'value_expr' in value_field:
            continue
        value_id = value_field['value_id']
        value_data = dict(sum=aggs_data['sum_{}'.format(value_id)]['value'])
        result[value_id] = value_data
    for value_field in value_fields:
        if 'value_expr' in value_field:
            value_id = value_field['value_id']
            try:
                value_data = dict(sum=eval(value_field['value_expr'], {"__builtins__": None},
                                           {k: v['sum'] for k, v in result.items() if isinstance(v, dict)}))
            except ZeroDivisionError:
                value_data = {'sum': 0}
            value_data['field_name'] = value_field['value_name']
            result[value_id] = value_data
    return result
